package com.juyu.miliao.scheduler.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.juyu.miliao.common.constant.HomeVideoRedis;
import com.juyu.miliao.common.constant.HomeVideoTypeConstant;
import com.juyu.miliao.common.domain.dto.HomeVideoCacheKeyDTO;
import com.juyu.miliao.common.util.CommonUtil;
import com.juyu.miliao.common.util.HomeVideoUtil;
import com.juyu.miliao.scheduler.constant.AnswerStatusRedis;
import com.juyu.miliao.scheduler.constant.UserLineStatusConstant;
import com.juyu.miliao.scheduler.dao.mysql.HomeVideoDao;
import com.juyu.miliao.scheduler.dao.mysql.UserInfoDao;
import com.juyu.miliao.scheduler.dao.redis.LineTimeRedisDao;
import com.juyu.miliao.scheduler.domin.User;
import com.juyu.miliao.scheduler.domin.UserVideo;
import com.juyu.miliao.scheduler.service.HomeVideoCacheService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.juyu.miliao.common.constant.HomeVideoRedis.DEFAULT_GLOBAL_KEY_VALUE;
import static com.juyu.miliao.common.constant.HomeVideoRedis.GLOBAL_KEY_INCREMENT;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

/**
 * 首页视频缓存
 *
 * @author jh
 * 2018/7/25
 */
@Log4j2
@Component
public class HomeVideoCacheServiceImpl implements HomeVideoCacheService {

    @Resource
    private HomeVideoDao homeVideoDao;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private LineTimeRedisDao lineTimeRedisDao;

    @Resource
    private UserInfoDao userInfoDao;


    @Override
    @Scheduled(cron = "0 0/5 * * * ? ")
    public void recommend() {
        doJob(true, HomeVideoTypeConstant.ON_LINE);
        doJob(true, HomeVideoTypeConstant.VEST);
    }

    @Override
    @Scheduled(cron = "0 0/5 * * * ? ")
    public void newest() {
        doJob(false, HomeVideoTypeConstant.ON_LINE);
        doJob(false, HomeVideoTypeConstant.VEST);
    }

    private List<UserVideo> listBySort(String sortConstant) {
        //置顶videoIds
        List<Long> topVideoIds = new ArrayList<>();
        //非置顶
        List<Long> otherIds = new ArrayList<>();

        //取sort
        Set<ZSetOperations.TypedTuple<String>> set = stringRedisTemplate.opsForZSet().reverseRangeWithScores(sortConstant, 0, -1);
        if (!CommonUtil.isEmptyCollection(set)) {
            Iterator<ZSetOperations.TypedTuple<String>> iterator = set.iterator();
            while (iterator.hasNext()) {
                ZSetOperations.TypedTuple<String> z = iterator.next();
                //置顶
                if (z.getScore() != null) {
                    Long v = CommonUtil.null2Long(z.getValue());
                    if (z.getScore() > 0) {
                        topVideoIds.add(v);
                    } else {
                        otherIds.add(v);
                    }
                }
            }
        }

        //置顶
        List<UserVideo> all = new ArrayList<>();
        UserVideo topVideo;
        for (Long id : topVideoIds) {
            topVideo = homeVideoDao.getVideoWithUser(id);
            if (topVideo != null) {
                setVideoLineStatus(topVideo);
                all.add(topVideo);
            }
        }

        //其他
        List<UserVideo> others = homeVideoDao.listVideoWithUser(otherIds);
        //排点赞
        others = others.stream()
                .sorted(comparing(UserVideo::getLove).reversed())
                .collect(toList());
        //在线放前面
        all.addAll(sortByOnline(others));

        return all;
    }

    private void updateCache(String globalCacheKey, String videoCacheKey, String rankKey, String userKey, List<UserVideo> all) {
        //cache start
//        String globalKey = getGlobalKey(recommend ? GLOBAL_RECOMMEND_KEY : GLOBAL_NEWEST_KEY);
        String globalKey = getGlobalKey(globalCacheKey);

        int size = all.size();
        String newGlobalKey = getNewGlobalKey(globalKey);
//        String newCacheKey = (recommend ? RECOMMEND_VIDEO_CACHE : NEWEST_VIDEO_CACHE) + newGlobalKey;
//        String newVideoRankKey = (recommend ? RECOMMEND_VIDEO_RANK : NEWEST_VIDEO_RANK) + newGlobalKey;
//        String newVideoUserKey = (recommend ? RECOMMEND_VIDEO_USER : NEWEST_VIDEO_USER) + newGlobalKey;
        String newCacheKey = videoCacheKey + newGlobalKey;
        String newVideoRankKey = rankKey + newGlobalKey;
        String newVideoUserKey = userKey + newGlobalKey;
        UserVideo cacheUserVideo;
        for (int i = 0; i < size; i++) {
            cacheUserVideo = all.get(i);
            //缓存list
            stringRedisTemplate.opsForZSet().add(newCacheKey, JSONObject.toJSONString(cacheUserVideo), i);
            //video与rank
            stringRedisTemplate.opsForZSet().add(newVideoRankKey, String.valueOf(cacheUserVideo.getVideoId()), i);
            //user与video
            stringRedisTemplate.opsForZSet().add(newVideoUserKey, String.valueOf(cacheUserVideo.getVideoId()), cacheUserVideo.getUserId());
        }
        //超时
        stringRedisTemplate.expire(newCacheKey, 24, TimeUnit.HOURS);
        stringRedisTemplate.expire(newVideoRankKey, 24, TimeUnit.HOURS);
        stringRedisTemplate.expire(newVideoUserKey, 24, TimeUnit.HOURS);

        stringRedisTemplate.opsForValue().set(globalCacheKey, String.valueOf(newGlobalKey));

        //清空原有缓存
//        String oldCacheKey = (recommend ? RECOMMEND_VIDEO_CACHE : NEWEST_VIDEO_CACHE) + globalKey;
//        String oldVideoRankKey = (recommend ? RECOMMEND_VIDEO_RANK : NEWEST_VIDEO_RANK) + globalKey;
//        String oldVideoUserKey = (recommend ? RECOMMEND_VIDEO_USER : NEWEST_VIDEO_USER) + globalKey;

        String oldCacheKey = videoCacheKey + globalKey;
        String oldVideoRankKey = rankKey + globalKey;
        String oldVideoUserKey = userKey + globalKey;

        stringRedisTemplate.opsForZSet().removeRange(oldCacheKey, 0, -1);
        stringRedisTemplate.opsForZSet().removeRange(oldVideoRankKey, 0, -1);
        stringRedisTemplate.opsForZSet().removeRange(oldVideoUserKey, 0, -1);
    }


    /**
     * 定时器任务 规则:1sort中取 2置顶 3在线 4点赞
     *
     * @param recommend 是否为推荐
     */
    private void doJob(boolean recommend, String homeVideoType) {
        log.info(String.format("定时器:首页视频:%s start", recommend ? "recommend" : "newest"));

        HomeVideoCacheKeyDTO dto = HomeVideoUtil.getHomeVideoCacheKeyDTO(recommend, homeVideoType);
        List<UserVideo> all = listBySort(dto.getSortConstant());
        if (!all.isEmpty()) {
            updateCache(dto.getJointGlobalKey(), dto.getJointVideoKey(), dto.getJointRankKey(), dto.getJointUserKey(), all);
        }

        log.info("定时器:首页视频:数据:size:" + all.size());
        log.info(String.format("定时器:首页视频:%s end", recommend ? "recommend" : "newest"));
    }

    /**
     * 获取新的全局Key
     *
     * @param globalKey globalKey
     * @return string
     */
    private String getNewGlobalKey(String globalKey) {
        int newGlobalKey = Integer.parseInt(globalKey) + GLOBAL_KEY_INCREMENT;
        if (newGlobalKey == Integer.MAX_VALUE) {
            newGlobalKey = DEFAULT_GLOBAL_KEY_VALUE;
        }
        return String.valueOf(newGlobalKey);
    }

    /**
     * 获取全局key
     *
     * @param key GLOBAL_RECOMMEND_KEY | GLOBAL_NEWEST_KEY
     * @return globalKey
     */
    @Override
    public String getGlobalKey(String key) {
        String globalKey = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isEmpty(globalKey)) {
            stringRedisTemplate.opsForValue().set(key, String.valueOf(HomeVideoRedis.DEFAULT_GLOBAL_KEY_VALUE));
            globalKey = stringRedisTemplate.opsForValue().get(key);
        }
        return globalKey;
    }

    /**
     * 在线的优先排序
     *
     * @param list List<UserVideo>
     * @return List<UserVideo>
     */
    private List<UserVideo> sortByOnline(List<UserVideo> list) {
        Integer size = list.size();
        List<UserVideo> result = new ArrayList<>(size);

        //查询并把在线的优先排序  手动处理信息
        for (UserVideo video : list) {
            //是否在线
            setVideoLineStatus(video);
            //优先排序
            if (Objects.equals(video.getLineStatus(), UserLineStatusConstant.ON_LINE)) {
                result.add(video);
            }
        }

        //剩下的添加进result
        for (UserVideo video : list) {
            if (!Objects.equals(video.getLineStatus(), UserLineStatusConstant.ON_LINE)) {
                result.add(video);
            }
        }

        return result;
    }

    /**
     * 填充在线信息
     *
     * @param video video
     */
    private void setVideoLineStatus(UserVideo video) {
        Long videoUserId = CommonUtil.null2Long(video.getUserId());

        if (videoUserId != -1L) {
            Boolean isOnline = lineTimeRedisDao.isMember(CommonUtil.null2String(video.getUserId()));
            if (isOnline) {
                //判断视频User信息
                User user = userInfoDao.getUserLineStatus(videoUserId);
                if (user != null) {
                    //设置  并维护能否接听电话缓存
                    String answerFlag = AnswerStatusRedis.ANSWER_FALSE;
                    if (user.getIsVideo() == 2) {
                        video.setLineStatus(UserLineStatusConstant.NOT_DISTURB);
                    } else {
                        video.setLineStatus(UserLineStatusConstant.ON_LINE);
                        answerFlag = AnswerStatusRedis.ANSWER_TRUE;
                    }
                    stringRedisTemplate.opsForValue().set(AnswerStatusRedis.ANSWER_STATUS + videoUserId, answerFlag);
                }
            }
        }
        if (video.getLineStatus() == null) {
            video.setLineStatus(UserLineStatusConstant.OUT_LINE);
        }
    }
}
